package cn.edu.xmu.oomall.liquidation.dao;

import cn.edu.xmu.oomall.core.util.Common;
import cn.edu.xmu.oomall.core.util.ReturnNo;
import cn.edu.xmu.oomall.core.util.ReturnObject;
import cn.edu.xmu.oomall.liquidation.mapper.ExpenditureItemPoMapper;
import cn.edu.xmu.oomall.liquidation.mapper.LiquidationPoMapper;
import cn.edu.xmu.oomall.liquidation.mapper.RevenueItemPoMapper;
import cn.edu.xmu.oomall.liquidation.model.bo.ExpenditureItem;
import cn.edu.xmu.oomall.liquidation.model.bo.Liquidation;
import cn.edu.xmu.oomall.liquidation.model.bo.RevenueItem;
import cn.edu.xmu.oomall.liquidation.model.bo.liquid.util.LiquidUtil;
import cn.edu.xmu.oomall.liquidation.model.po.*;
import cn.edu.xmu.oomall.liquidation.model.vo.*;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static cn.edu.xmu.privilegegateway.annotation.util.Common.*;


@Repository
public class LiquidationDao {
    private Logger logger = LoggerFactory.getLogger(LiquidationDao.class);
    @Autowired
    private LiquidationPoMapper liquidationPoMapper;

    @Autowired
    private RevenueItemPoMapper revenueItemPoMapper;

    @Autowired
    private ExpenditureItemPoMapper expenditureItemPoMapper;

    public ReturnObject getSimpleLiquidation(Long shopId, LocalDateTime beginDate, LocalDateTime endDate, Boolean state, Integer page, Integer pageSize) {
        try {
            PageHelper.startPage(page, pageSize);
            LiquidationPoExample liquidationPoExample = new LiquidationPoExample();
            LiquidationPoExample.Criteria criteria = liquidationPoExample.createCriteria();
            criteria.andShopIdEqualTo(shopId);
            if (beginDate != null) {
                criteria.andLiquidDateGreaterThanOrEqualTo(beginDate);
            }
            if (endDate != null) {
                criteria.andLiquidDateLessThanOrEqualTo(endDate);
            }
            if (state != null) {
                criteria.andStateEqualTo((byte) (state ? 1:0));
            }

            List<LiquidationPo> liquidationPoList = liquidationPoMapper.selectByExample(liquidationPoExample);

            PageInfo<LiquidationPo> pageInfo = new PageInfo<>(liquidationPoList);
            ReturnObject returnObject = new ReturnObject<>(pageInfo);
            return Common.getPageRetVo(returnObject, SimpleLiquidationRetVo.class);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    public ReturnObject getLiquidationById(Long id) {
        try {
            LiquidationPo liquidationPo = liquidationPoMapper.selectByPrimaryKey(id);

            if (liquidationPo == null) {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
            }

            Liquidation liquidation = cloneVo(liquidationPo, Liquidation.class);
            return new ReturnObject(liquidation);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    public ReturnObject getLiquidation(LiquidationPoExample liquidationPoExample) {
        try {
            List<LiquidationPo> liquidationPoList = liquidationPoMapper.selectByExample(liquidationPoExample);
            ReturnObject returnObject = new ReturnObject(liquidationPoList);
            return Common.getListRetVo(returnObject, Liquidation.class);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    private String getShopNameByLiquidId(Long liquidId) {
        Liquidation liquidation = (Liquidation) getLiquidationById(liquidId).getData();
        return liquidation.getShopName();
    }

    private ReturnObject assembleGeneralLedger(List<? extends Object> poList) {
        List<GeneralLedgerPo> generalLedgerPoList = new ArrayList<>(poList.size());
        for(Object po : poList) {
            generalLedgerPoList.add(cloneVo(po, GeneralLedgerPo.class));
        }
        PageInfo pageInfo = new PageInfo(poList);
        ReturnObject returnObject = new ReturnObject(pageInfo);

        ReturnObject voReturnObject = Common.getPageRetVo(returnObject, GeneralLedgerRetVo.class);
        Map<String, Object> map = (Map<String, Object>) voReturnObject.getData();
        List<Object> voList = (List<Object>) map.get("list");
        // 组装shop
        String shopName;
        for(int i = 0; i < voList.size(); i++) {
            GeneralLedgerPo generalLedgerPo = generalLedgerPoList.get(i);
            GeneralLedgerRetVo generalLedgerRetVo = (GeneralLedgerRetVo)voList.get(i);
            shopName = getShopNameByLiquidId(generalLedgerPo.getLiquidId());
            generalLedgerRetVo.setShop(new SimpleShopRetVo(generalLedgerPo.getShopId(), shopName));
            generalLedgerRetVo.setProduct(new SimpleProductRetVo(generalLedgerPo.getProductId(), generalLedgerPo.getProductName()));
        }

        return voReturnObject;
    }

    public ReturnObject getRevenueItemUsePage(RevenueItemPoExample revenueItemPoExample, Integer page, Integer pageSize) {
        try {
            PageHelper.startPage(page, pageSize);
            List<RevenueItemPo> revenueItemPoList = revenueItemPoMapper.selectByExample(revenueItemPoExample);

            return assembleGeneralLedger(revenueItemPoList);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    public ReturnObject getExpenditureUsePage(ExpenditureItemPoExample expenditureItemPoExample, Integer page, Integer pageSize) {
        try {
            PageHelper.startPage(page, pageSize);
            List<ExpenditureItemPo> expenditureItemPoList = expenditureItemPoMapper.selectByExample(expenditureItemPoExample);

            return assembleGeneralLedger(expenditureItemPoList);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    public ReturnObject getExpenditureById(Long id) {
        try {
            ExpenditureItemPo expenditureItemPo = expenditureItemPoMapper.selectByPrimaryKey(id);
            if (expenditureItemPo == null) {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
            }

            LiquidationPo liquidationPo = liquidationPoMapper.selectByPrimaryKey(expenditureItemPo.getLiquidId());
            ExpenditureItem expenditureItem = cloneVo(expenditureItemPo, ExpenditureItem.class);
            expenditureItem.setShopName(liquidationPo.getShopName());

            return new ReturnObject(expenditureItem);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }

    }

    public ReturnObject getLiquidation(Long shopId, LocalDateTime liquidDateTime) {
        try {
            LiquidationPoExample liquidationPoExample = new LiquidationPoExample();
            LiquidationPoExample.Criteria criteria = liquidationPoExample.createCriteria();
            criteria.andShopIdEqualTo(shopId);
            criteria.andLiquidDateEqualTo(liquidDateTime);
            List<LiquidationPo> liquidationPoList = liquidationPoMapper.selectByExample(liquidationPoExample);
            if(liquidationPoList.isEmpty()) {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
            }
            return new ReturnObject(cloneVo(liquidationPoList.get(0), Liquidation.class));
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    private void setRevenueItemDefault(RevenueItem revenueItem) {
        if (revenueItem.getAmount() == null) {
            revenueItem.setAmount(0L);
        }
        if (revenueItem.getQuantity() == null) {
            revenueItem.setQuantity(0L);
        }
        if (revenueItem.getExpressFee() == null) {
            revenueItem.setExpressFee(0L);
        }
        if (revenueItem.getCommission() == null) {
            revenueItem.setCommission(0L);
        }
        if (revenueItem.getPoint() == null) {
            revenueItem.setPoint(0L);
        }
        if (revenueItem.getShopRevenue() == null) {
            revenueItem.setShopRevenue(0L);
        }
    }

    public ReturnObject insertRevenue(RevenueItem revenueItem) {
        try {
            setRevenueItemDefault(revenueItem);
            RevenueItemPo revenueItemPo = cloneVo(revenueItem, RevenueItemPo.class);

            setPoCreatedFields(revenueItemPo, LiquidUtil.getUserId(), LiquidUtil.getUserName());
            revenueItemPoMapper.insertSelective(revenueItemPo);
            return new ReturnObject(ReturnNo.OK);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }

    public ReturnObject updateLiquid(Liquidation liquidation) {
        try {
            LiquidationPo liquidationPo = cloneVo(liquidation, LiquidationPo.class);

            setPoModifiedFields(liquidationPo, LiquidUtil.getUserId(), LiquidUtil.getUserName());
            liquidationPoMapper.updateByPrimaryKeySelective(liquidationPo);

            return new ReturnObject(ReturnNo.OK);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }

    public ReturnObject getRevenueItem(RevenueItemPoExample revenueItemPoExample) {
        try {
            List<RevenueItemPo> revenueItemPoList = revenueItemPoMapper.selectByExample(revenueItemPoExample);
            if (revenueItemPoList.isEmpty()) {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
            }
            ArrayList<RevenueItem> revenueItemList = new ArrayList<>(revenueItemPoList.size());
            for(RevenueItemPo revenueItemPo : revenueItemPoList) {
                revenueItemList.add(cloneVo(revenueItemPo, RevenueItem.class));
            }
            return new ReturnObject(revenueItemList);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }

    private void setExpenditureItemDefault(ExpenditureItem expenditureItem) {
        if (expenditureItem.getAmount() == null) {
            expenditureItem.setAmount(0L);
        }
        if (expenditureItem.getQuantity() == null) {
            expenditureItem.setQuantity(0L);
        }
        if (expenditureItem.getExpressFee() == null) {
            expenditureItem.setExpressFee(0L);
        }
        if (expenditureItem.getCommission() == null) {
            expenditureItem.setCommission(0L);
        }
        if (expenditureItem.getPoint() == null) {
            expenditureItem.setPoint(0L);
        }
        if (expenditureItem.getShopRevenue() == null) {
            expenditureItem.setShopRevenue(0L);
        }
    }

    public ReturnObject insertExpenditure(ExpenditureItem expenditureItem) {
        try {
            setExpenditureItemDefault(expenditureItem);
            ExpenditureItemPo expenditureItemPo = cloneVo(expenditureItem, ExpenditureItemPo.class);

            setPoCreatedFields(expenditureItemPo, LiquidUtil.getUserId(), LiquidUtil.getUserName());
            expenditureItemPoMapper.insertSelective(expenditureItemPo);
            return new ReturnObject(ReturnNo.OK);
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }

    private ReturnObject insertLiquidation(LiquidationPo liquidationPo) {
        try {
            liquidationPoMapper.insertSelective(liquidationPo);
            return new ReturnObject(cloneVo(liquidationPo, Liquidation.class));
        } catch (Exception e) {
            logger.error(e.getMessage());
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }

    public ReturnObject insertLiquidation(Liquidation liquidation) {
        LiquidationPo liquidationPo = cloneVo(liquidation, LiquidationPo.class);
        liquidationPo.setState((byte) 0);

        setPoCreatedFields(liquidationPo, LiquidUtil.getUserId(), LiquidUtil.getUserName());
        return insertLiquidation(liquidationPo);
    }
}